﻿using System;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace WiiShooter
{
    class Camera
    {
        private Microsoft.DirectX.Direct3D.Device device;
        public Vector3 cameraPosition;
        private Vector3 previousCameraPosition;
        public Vector3 cameraTarget;
        private Vector3 cameraUpVector;
        private float radius = 1f;
        private float moveDist = 1f;
        private float hRadians;
        private float vRadians;
        private static Vector3 defaultview = new Vector3(0, 0, 0);
        private LevelLoader theLevelLoader;
        private float theMovementFactor;
        private float theHeight;

        public Camera(ref Microsoft.DirectX.Direct3D.Device devicePass, LevelLoader origLevelLoader,
            float movementFactor, float height)
        {
            device = devicePass;
            theLevelLoader = origLevelLoader;
            theMovementFactor = movementFactor;

            cameraPosition = origLevelLoader.thePlayer.centerCoords;
            previousCameraPosition = new Vector3(0, 0, 0);
            setCamera(cameraPosition, 0, 0);
            theHeight = height;
        }

        public void setCamera(Vector3 cameraPosition, float h, float v)
        {
            // The camera target is going to be straight ahead along the z-axis.
            cameraTarget = new Vector3(cameraPosition.X, 0, Int16.MaxValue);
            cameraUpVector = new Vector3(0, 0, Int16.MaxValue);

            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, 1, 1f, 300f);
            device.Transform.View = Matrix.LookAtLH(cameraPosition, cameraTarget, cameraUpVector);
            device.RenderState.Lighting = false;
            device.RenderState.CullMode = Cull.None;

            hRadians = h;
            vRadians = v;

            rotateCamera((float)Math.PI / 2, 0);
        }

        /**
         * rotateCamera - Method which is called when the camera needs to be pivoted on the Z-axis.
         * @param h - How much to pivot horizontally.
         * @param v - How much to pivot vertically.
         */
        public void rotateCamera(float h, float v)
        {
            hRadians += h;
            vRadians += v;

            cameraTarget.Y = cameraPosition.Y + (float)(radius * Math.Sin(vRadians));
            cameraTarget.X = cameraPosition.X + (float)(radius * Math.Cos(vRadians) * Math.Cos(hRadians));
            cameraTarget.Z = cameraPosition.Z + (float)(radius * Math.Cos(vRadians) * Math.Sin(hRadians));

            cameraUpVector.X = cameraPosition.X - cameraTarget.X;
            cameraUpVector.Y = Math.Abs(cameraPosition.Y + (float)(radius * Math.Sin(vRadians + Math.PI / 2)));
            cameraUpVector.Z = cameraPosition.Z - cameraTarget.Z;
        }

        public void slideCamera(float h, float v)
        {
            cameraPosition.Y += v * moveDist;
            cameraPosition.X += h * moveDist * (float)Math.Cos(hRadians + Math.PI / 2);
            cameraPosition.Z += h * moveDist * (float)Math.Sin(hRadians + Math.PI / 2);
            rotateCamera(0, 0);
        }

        public void moveCamera(float d)
        {
            cameraPosition.Y += d * moveDist * (float)Math.Sin(vRadians);
            cameraPosition.X += d * moveDist * (float)(Math.Cos(vRadians) * Math.Cos(hRadians));
            cameraPosition.Z += d * moveDist * (float)(Math.Cos(vRadians) * Math.Sin(hRadians));
            rotateCamera(0, 0);
        }

        public void aboutToMove()
        {
            previousCameraPosition.X = cameraPosition.X;
            previousCameraPosition.Y = cameraPosition.Y;
            previousCameraPosition.Z = cameraPosition.Z;
        }

        public Vector3 getCameraPosition()
        {
            return cameraPosition;
        }

        public void changeCamera()
        {
            try {
                device.Transform.View = Matrix.LookAtLH(cameraPosition, cameraTarget, cameraUpVector);
            } catch(NullReferenceException e) {
                Console.WriteLine("Error on closing " + e.Message);
            }
        }

        public float getVRadians()
        {
            return vRadians;
        }

        public float getHRadians()
        {
            return hRadians;
        }

        public void moveCameraAlongPath(float moveX, float moveY, float moveZ)
        {
            cameraPosition.X += moveX;
            cameraTarget.X += moveX;
            cameraPosition.Y += moveY;
            cameraTarget.Y += moveY;
            cameraPosition.Z += moveZ;
            cameraTarget.Z += moveZ;
            device.Transform.View = Matrix.LookAtLH(cameraPosition, cameraTarget, cameraUpVector);
        }
    }
}
